package taller2.model.graph.algorithms;

import java.util.ArrayList;

import taller2.interfaces.Connection;
import taller2.interfaces.Graph;
import taller2.interfaces.InitialNodeApplicator;
import taller2.interfaces.Node;
import taller2.utils.Constants;
import taller2.utils.Constants.Colores;

public class TopologicDFSApplicator extends AlgorithmApplicatorImpl implements InitialNodeApplicator{
	private static final String path = Constants.savedGraphsPath + "DFSTopo";
	private ArrayList<Node> visited;
	private ArrayList<Boolean> visitadosTemp;
	private ArrayList<Node> listaResultante;
	
	Graph graph;
	
	private int initialNode;
	private int cont;
	private boolean directed;
	
	public TopologicDFSApplicator(){
		super();
		directed = true;
	}

	public void solveProblem(Graph g){

		graph = g;
		
		if(noGraph(graph) || !noGraphPaths() || !noPseudocodeLines())
			return;
		
		//el algoritmo es solo para grafos acíclicos!
		if (! esAciclico(graph)){
			auxText.add("ATENCION! SOLO PARA GRAFOS ACICLICOS");
			return;
		}
		
		graph.unselectConnections();
		graph.unselectNodes();
		graph.killFluxes();
		graph.killWeights();
		directed = graph.isDirected();
			
		visited = new ArrayList<Node>();
		listaResultante = new ArrayList<Node>();
		visitadosTemp = new ArrayList<Boolean>();

		ArrayList<Node> nodes = graph.getNodes();

		Node start;
		if (initialNode != -1)
			start = nodes.get(initialNode);	        
		else
			start = graph.getFirstNode();	

		for(int i=0; i<nodes.size() ; i++){
			Node node = nodes.get(i);
			node.setId(i);

			visitadosTemp.add(false);
		}
		
		//Salvo estado inicial
		graph.save(path);
		cont = 0;
		
		guardarPaso(graph, 1);

        //mientras no esten todos visitados
		while(visited.size() != graph.getNodes().size()){
			guardarPaso(graph, 3);
			
			guardarPaso(graph, 4);
			visitar(start);
			
			//y busco nuevo nodo inicial
			for(Node n: nodes){
				if  (! visited.contains(n)) {
					start = n;
					break;
				}
			}
		}
		
		guardarPaso(graph, 13);
		//Listo!, se ejecuto el algoritmo -> cargo el estado inicial para empezar a mostrarlo
		graph.load(path);
	}
	
	private void visitar(Node node){
		guardarPaso(graph, 7);
		if ( (! visited.contains(node)) && ( visitadosTemp.get(node.getNumberId()) == false) ){
			visitadosTemp.set(node.getNumberId(), true);
			
			guardarPaso(graph, 8);
			for (Connection connection : node.getConnections()) {
				guardarPaso(graph, 9);
				Node other = connection.getOtherNode();
				visitar(other);
			}
			visited.add(node);
			
			guardarPaso(graph, 10);
			node.changeColor(Colores.red);

			
			visitadosTemp.set(node.getNumberId(), false);
			listaResultante.add(0, node);
			guardarPaso(graph, 11);
		}
		
	}

	public void setInitialNode(int selectedNode) {
		initialNode = selectedNode;
	}

	private String formatearVisitados(){
		String visitedList = "\n";
		for(Node node:listaResultante){
			String tag = node.getTag();
			if (tag == null) { tag = Constants.sinTag;}
			visitedList += tag + " ";
		}
		return visitedList;
	}
	
    public String getAuxText(){
    	if(auxText.isEmpty())
    		return "";
    	return auxText.get(position);
    }
    
	private void guardarPaso(Graph g, int pcLine){
		cont++;
		saveGraph(g, path + cont);
		pseudocodeLines.add(pcLine);
		auxText.add( "Visitados:" + formatearVisitados());
		
		return;
	}
    
	public void save(String path){
	};

	public void load(String path){
	}

	@Override
	public int getHighlightedWordNumber() {
		return -1;
	};
	
	public boolean esAciclico(Graph g){
		
		boolean encontrado = false;
						
		for(Node start : g.getNodes()){
								
			encontrado = findCyclesStartingWith(start,start,g);
			if (encontrado)
				break;
		}
			
		if (encontrado)
			 //se encontro un ciclo -> no es aciclico
			return false;
		else
			return true;
	}
	
	private boolean findCyclesStartingWith(Node start,Node act,Graph g){
		
		if(!act.isSelected()){
			act.mark();
		}
		
		for(Connection connection : act.getConnections()){
			Node otherNode = connection.getOtherNode();
			
			boolean areEqual = otherNode.equals(start);

			if(areEqual){
				return true;				
			}

			boolean unused = !otherNode.isMarked() && !otherNode.isSelected();
			if(unused){
				if(findCyclesStartingWith(start,otherNode,g)){
					return true;							
				}
			}
		}
		return false;
	}

	public String getStartingGraphPath() {
		if(graphPaths.isEmpty())
			return "";
		return graphPaths.get(0);
	}

	public boolean wasDirected() {
		return directed;
	}
}
